﻿using System;
using System.Linq;
using System.Web;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using EPiServer;
using EPiServer.Core;
using PageTypeBuilder;
using TemplateFoundation.Configuration;
using TemplateFoundation.Core;
using TemplateFoundation.Exceptions;
using TemplateFoundation.PageTypes;
using TemplateFoundation.Reflection;

namespace TemplateFoundation.MasterPages
{
    /// <summary>
    /// Base class for master pages
    /// </summary>
    public abstract class MasterPageBase : System.Web.UI.MasterPage
    {
        private int? _titleElementIndex;

        /// <summary>
        /// Gets the current page
        /// </summary>
        public PageTypeBase CurrentPage
        {
            get
            {
                var pageHandler = HttpContext.Current.CurrentHandler as PageBase;

                if (pageHandler==null)
                {
                    throw new TemplateFoundationException(
                        "The current HttpContext doesn't appear to be an EPiServer PageBase object, unable to retrieve the current page");
                }

                return pageHandler.CurrentPage as PageTypeBase;
            }
        }

        /// <summary>
        /// Gets the title to use for the title tag
        /// </summary>
        /// <remarks>The company name, if specified, is automatically appended to the page title (does not apply to the start page)</remarks>
        protected virtual string Title
        {
            get
            {
                if (!string.IsNullOrEmpty(Settings.Instance.ContactInformation.CompanyName) && CurrentPage.PageLink!=PageReference.StartPage)
                {
                    return string.Format("{0} - {1}", CurrentPage.Title, Settings.Instance.ContactInformation.CompanyName);
                }

                return CurrentPage.Title;
            }
        }

        /// <summary>
        /// Raises the <see cref="E:System.Web.UI.Control.Init"/> event.
        /// </summary>
        /// <param name="e">An <see cref="T:System.EventArgs"/> object that contains the event data.</param>
        protected override void OnInit(EventArgs e)
        {
            base.OnInit(e);

            if (CurrentPage==null) // Current page doesn't inherit from PageTypeBase
            {
                return;
            }

            // Ensure meta tags are only set up once
            if (Page.Master!=this)
            {
                return;
            }

            AddStylesheetLinks();

            AddJavaScriptLinks();

            AddRssLinks();

            AddMetaTags();
        }

        /// <summary>
        /// Adds stylesheet links to the head tag
        /// </summary>
        /// <remarks>Uses the StaticFilesPath setting, defaults to "/Templates/"</remarks>
        protected void AddStylesheetLinks()
        {
            if (!Settings.Instance.DisableStylesheetLinks)
            {
                Page.Header.Controls.AddAt(TitleElementIndex+1, new Literal { Text = Environment.NewLine });

                // Add main stylesheet
                var mainStylesheet = new HtmlLink
                                         {
                                             Href = Settings.Instance.GetMainStylesheetPath(!HttpContext.Current.IsDebuggingEnabled)
                                         };
                mainStylesheet.Attributes.Add("type", "text/css");
                mainStylesheet.Attributes.Add("rel", "stylesheet");
                mainStylesheet.Attributes.Add("media", "screen,projection");
                Page.Header.Controls.AddAt(TitleElementIndex+2, mainStylesheet);

                Page.Header.Controls.AddAt(TitleElementIndex+3, new Literal { Text = Environment.NewLine });

                // Add print stylesheet
                var printStylesheet = new HtmlLink
                                          {
                                              Href = Settings.Instance.GetStylesheetPath("print", !HttpContext.Current.IsDebuggingEnabled)
                                          };
                printStylesheet.Attributes.Add("type", "text/css");
                printStylesheet.Attributes.Add("rel", "stylesheet");
                printStylesheet.Attributes.Add("media", "print");
                Page.Header.Controls.AddAt(TitleElementIndex+4, printStylesheet);

                Page.Header.Controls.AddAt(TitleElementIndex+5, new Literal { Text = Environment.NewLine });
            }
        }

        /// <summary>
        /// Adds JavaScript links to the head tag
        /// </summary>
        /// <remarks>Uses the StaticFilesPath setting, defaults to "/Templates/"</remarks>
        protected virtual void AddJavaScriptLinks()
        {
            if (!Settings.Instance.DisableJavaScriptLinks)
            {
                // Add main script file
                var mainScript = new HtmlGenericControl("script");
                mainScript.Attributes.Add("src",
                    Settings.Instance.GetMainJavaScriptPath(!HttpContext.Current.IsDebuggingEnabled));

                mainScript.Attributes.Add("type", "text/javascript");
                Page.Header.Controls.Add(mainScript);

                Page.Header.Controls.Add(new Literal { Text = Environment.NewLine });
            }
        }

        /// <summary>
        /// Adds meta tags to the head tag
        /// </summary>
        protected virtual void AddMetaTags()
        {
            // Add title tag
            EnsureTitleElementExists();

            // Add canonical tag
            AddCanonicalTag(CurrentPage.GetCanonicalUrl(true));

            // Add robots meta tag
            AddMetaTag("robots", string.Format("{0},{1}",
                                 CurrentPage.EnableIndexing ? "index" : "noindex",
                                 CurrentPage.EnableLinkFollowing ? "follow" : "nofollow"));

            // Add author meta tag
            if (!string.IsNullOrEmpty(CurrentPage.Author))
            {
                AddMetaTag("author", CurrentPage.Author);
            }

            // Add keywords meta tag
            if (!string.IsNullOrEmpty(CurrentPage.Keywords))
            {
                AddMetaTag("keywords", CurrentPage.Keywords);
            }

            // Add description meta tag
            if (!string.IsNullOrEmpty(CurrentPage.Description))
            {
                AddMetaTag("description", CurrentPage.Description);
            }

            // Add copyright meta tag
            if (!string.IsNullOrEmpty(CurrentPage.Copyright))
            {
                AddMetaTag("copyright", CurrentPage.Copyright);
            }

            // Add content language meta tag
            AddMetaTag("content-language", CurrentPage.LanguageBranch);

            // Add TemplateFoundation meta tag
            AddMetaTag("generator", "EPiServer with ETF");
        }

        /// <summary>
        /// Adds RSS auto-discovery links to the head tag
        /// </summary>
        protected virtual void AddRssLinks()
        {
            var rssPageTypes = PageTypeInheritance.FindPageTypeFamily(typeof(RssPageBase));
            
            var rssFeeds = PageFactory.Instance.GetPagesByType(rssPageTypes, true);

            foreach (var feed in rssFeeds)
            {
                var feedPage = (RssPageBase)feed;

                if (!feedPage.DisableAutoDiscovery)
                {
                    // Link to the friendly URL of the feed, or the alternate URL if one is specified
                    var rssLink = new HtmlLink
                                      {
                                          Href = string.IsNullOrEmpty(feedPage.AlternateUrl)
                                                     ? feedPage.GetFriendlyUrl(true)
                                                     : feedPage.AlternateUrl
                                      };

                    rssLink.Attributes.Add("rel", "alternate");
                    rssLink.Attributes.Add("type", "application/rss+xml");
                    rssLink.Attributes.Add("title", ((PageTypeBase) feed).Title);

                    Page.Header.Controls.AddAt(TitleElementIndex+1, new Literal { Text = Environment.NewLine });

                    Page.Header.Controls.AddAt(TitleElementIndex+2, rssLink);
                }
            }
        }

        #region Helper methods

        /// <summary>
        /// Adds a canonical tag to the page
        /// </summary>
        /// <param name="url">The full canonical URL</param>
        protected virtual void AddCanonicalTag(string url)
        {
            Page.Header.Controls.AddAt(TitleElementIndex+1, new Literal { Text = Environment.NewLine });
            
            var link = new HtmlLink
            {
                Href = url
            };

            link.Attributes.Add("rel", "canonical");

            Page.Header.Controls.AddAt(TitleElementIndex+2, link);
        }

        /// <summary>
        /// Adds a meta tag to the head tag
        /// </summary>
        /// <param name="name"></param>
        /// <param name="value"></param>
        protected virtual void AddMetaTag(string name, string value)
        {
            Page.Header.Controls.AddAt(TitleElementIndex+1, new Literal { Text = Environment.NewLine });

            Page.Header.Controls.AddAt(TitleElementIndex+2, new HtmlMeta
            {
                Name = name,
                Content = value
            });
        }

        /// <summary>
        /// Ensures a title element exists in the header
        /// </summary>
        private void EnsureTitleElementExists()
        {
            var titleElement = Page.Header.Controls.OfType<HtmlTitle>().FirstOrDefault();

            if (titleElement==null)
            {
                var title = new HtmlTitle { Text = Title };

                Page.Header.Controls.AddAt(0, title);
            }
            else
            {
                titleElement.Text = Title;
            }
        }

        /// <summary>
        /// Returns the index of the title element inside the head element
        /// </summary>
        /// <returns></returns>
        protected virtual int TitleElementIndex
        {
            get
            {
                if (!_titleElementIndex.HasValue)
                {
                    var titleElement = Page.Header.Controls.OfType<HtmlTitle>().FirstOrDefault();

                    _titleElementIndex = titleElement != null ? Page.Header.Controls.IndexOf(titleElement) : 0;
                }

                return _titleElementIndex.Value;
            }
        }

        #endregion
    }
}
